<?php

declare(strict_types=1);

namespace Zlf\AppValidate\Rule;

use Zlf\AppException\Exception\ValidateException;

/**
 * 正则验证器
 * 使用正则表达式验证输入值是否匹配指定的模式。
 * 支持结果取反，即验证是否不匹配指定模式。
 */
class MatchValidate extends RuleAbstract
{
    /**
     * 正则表达式
     * 用于验证输入值是否匹配的正则表达式模式。
     * @var string
     */
    protected $_pattern;

    /**
     * 结果取反
     * 如果设置为 true，则验证结果取反，即验证是否不匹配指定模式。
     * @var bool
     */
    protected $_not = false;

    /**
     * 默认错误消息
     * 当验证失败时返回的错误信息模板。
     * @var string
     */
    protected $_error = '{label}验证失败';

    /**
     * 设置正则表达式
     * @param string $pattern 正则表达式模式
     * @return MatchValidate
     */
    public function setPattern(string $pattern): MatchValidate
    {
        $this->_pattern = $pattern;
        return $this;
    }

    /**
     * 设置结果是否取反
     * @param bool $not 是否取反验证结果
     * @return MatchValidate
     */
    public function setNot(bool $not): MatchValidate
    {
        $this->_not = $not;
        return $this;
    }

    /**
     * 验证输入值是否匹配正则表达式
     * @param mixed $value 输入值
     * @param string $label 验证名（用于错误信息中）
     * @return bool 验证结果
     * @throws ValidateException 如果未配置正则表达式时抛出异常
     */
    public function validate($value, string $label = ''): bool
    {
        // 确保输入值是字符串或数字
        if (is_string($value) || is_numeric($value)) {
            // 如果值为空且 `_noSkipping` 为 true，则直接返回 true
            if (empty($value) && $this->_noSkipping === true) {
                $this->_value = $value;
                return true;
            }

            // 检查是否配置了正则表达式
            if (!$this->_pattern) {
                throw new ValidateException('请配置' . $label . '验证表达式');
            }

            // 使用正则表达式验证输入值
            $status = (bool)preg_match($this->_pattern, strval($value));

            // 如果设置了取反，则反转验证结果
            if ($this->_not) {
                $status = !$status;
            }

            // 如果验证通过，保存验证结果并返回 true
            if ($status) {
                $this->_value = $value;
                return true;
            }
        }

        // 如果验证失败，添加错误信息并返回 false
        return $this->addError($this->_error, $label);
    }
}
